package com.xjd.edu.toolkit.helper;

import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * Optional 增强实现（部分语法参考 Kotlin 实现），类本身与 Optional 无关联，属于重写实现
 *
 * @author wangyu
 * @created 2025/3/15 13:53
 * @see Optional
 */
public final class GameOptional<T> {

    private static final GameOptional<?> EMPTY = new GameOptional<>();

    private final T value;

    private GameOptional() {
        this.value = null;
    }

    public static <T> GameOptional<T> empty() {
        return (GameOptional<T>) EMPTY;
    }

    private GameOptional(T value) {
        this.value = Objects.requireNonNull(value);
    }

    public static <T> GameOptional<T> of(T value) {
        return new GameOptional<>(value);
    }

    public static <T> GameOptional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }

    public static <T> GameOptional<T> ofNullable(Optional<T> optional) {
        return optional.map(GameOptional::of).orElseGet(GameOptional::empty);
    }

    public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }

    public boolean isPresent() {
        return value != null;
    }

    public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }

    public GameOptional<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();
    }

    public <U> GameOptional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return GameOptional.ofNullable(mapper.apply(value));
        }
    }

    public <U> GameOptional<U> flatMap(Function<? super T, GameOptional<U>> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Objects.requireNonNull(mapper.apply(value));
        }
    }

    public T orElse(T other) {
        return value != null ? value : other;
    }

    public T orElseGet(Supplier<? extends T> other) {
        return value != null ? value : other.get();
    }

    /**
     * 值不存在时执行一个 Supplier 动作，使调用链仍可继续执行
     *
     * @param supplier
     * @return
     */
    public GameOptional<T> orElseApply(Supplier<T> supplier) {
        Objects.requireNonNull(supplier);
        if (isPresent()) {
            return this;
        }
        return GameOptional.ofNullable(supplier.get());
    }

    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (isPresent()) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }

    /**
     * 值非空时执行 Consumer 闭包，否则不作任何处置，区别于 #map(Function) 其强调的是自身影响而非转换
     *
     * @param consumer
     * @return
     * @see #map(Function)
     */
    public GameOptional<T> apply(Consumer<? super T> consumer) {
        Objects.requireNonNull(consumer);
        if (isPresent()) {
            consumer.accept(this.value);
        }
        return this;
    }

//    /**
//     * 值为空时执行 Runnable 闭包，否则不作任何处置
//     *
//     * @param runnable
//     * @return
//     */
//    public GameOptional<T> applyIfAbsent(Runnable runnable) {
//        Objects.requireNonNull(runnable);
//        if (!isPresent()) {
//            runnable.run();
//        }
//        return this;
//    }

    /**
     * 满足条件返回值，否则返回空，与 #filter(Predicate) 类似，但区别在于这是一个终态操作符
     *
     * @param predicate
     * @return 注：可能返回空值
     * @see #filter(Predicate)
     */
    public T takeIf(Predicate<T> predicate) {
        Objects.requireNonNull(predicate);
        return predicate.test(this.value) ? this.value : null;
    }

    /**
     * 不满足条件返回值，否则返回空，与 #filter(Predicate) 类似，但区别在于这是一个终态操作符
     *
     * @param predicate
     * @return 注：可能返回空值
     * @see #filter(Predicate)
     */
    public T takeUnless(Predicate<T> predicate) {
        Objects.requireNonNull(predicate);
        return predicate.test(this.value) ? null : this.value;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (!(obj instanceof GameOptional)) {
            return false;
        }

        GameOptional<?> other = (GameOptional<?>) obj;
        return Objects.equals(value, other.value);
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(value);
    }

    @Override
    public String toString() {
        return value != null
                ? String.format("GameOptional[%s]", value)
                : "GameOptional.empty";
    }

}
